package com.favccxx.search.application;

import com.favccxx.search.domain.calcite.CalciteService;
import com.favccxx.search.domain.conceptmodel.ConceptModel;
import com.favccxx.search.domain.datamapper.*;
import com.favccxx.search.domain.datamapper.horizontal.HorizontalMapper;
import com.favccxx.search.domain.datamapper.vertical.VerticalMapper;
import com.favccxx.search.infrastructure.constant.AppConstants;
import com.favccxx.search.infrastructure.constant.MapperConstants;
import com.favccxx.search.infrastructure.exception.AppException;
import com.favccxx.search.infrastructure.exception.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author favccxx
 */
@Slf4j
@Service
public class SearchService {

    @Autowired
    ConceptModelService conceptModelService;
    @Autowired
    DataMapperService mapperService;
    @Autowired
    CalciteService calciteService;

    public String searchData(String projectId, String conceptModeCode, List<String> fieldList, String whereClause, String orderClause) {
        DataMapper mapper = mapperService.findByProjectIdAndConceptModelCode(projectId, conceptModeCode);
        if (mapper == null) {
            throw new AppException(ExceptionCode.Search.INVALID_SEARCH_PARAM, "projectId or conceptModeCode invalid.");
        }

        try {
            String subSql = buildSubMapperSql(mapper);
            String searchSql = buildFullSearchSql(fieldList, subSql);
            if (StringUtils.isNotBlank(whereClause)) {
                if (whereClause.startsWith(AppConstants.SQL_WHERE)) {
                    searchSql = searchSql + whereClause;
                } else {
                    searchSql = searchSql + " where " + whereClause;
                }
            }

            log.info("search sql: " + searchSql);
            return calciteService.executeSql(searchSql);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new AppException(ExceptionCode.Search.SEARCH_FAILED, e.getMessage());
        }
    }

    /**
     * Get sql clause by mapper
     *
     * @param mapper
     * @return
     */
    private String buildSubMapperSql(DataMapper mapper) {
        String subSql = "";
        if (MapperConstants.HORIZONTAL_MAPPER_TYPE.equalsIgnoreCase(mapper.getMapperType())) {
            // Generate base sql
            if (StringUtils.isNotBlank(mapper.getQuerySql())) {
                subSql = mapper.getQuerySql();
            } else {
                subSql = mapperService.generateMapperSql(mapper);
            }
        } else if (MapperConstants.VERTICAL_MAPPER_TYPE.equalsIgnoreCase(mapper.getMapperType())) {
            subSql = mapperService.generateVerticalMapperSql(mapper);
        }
        return subSql;
    }

    /**
     * Build full search sql
     *
     * @param fieldList
     * @param subSql
     * @return
     * @example select id, name, value from (subsql)
     */
    private String buildFullSearchSql(List<String> fieldList, String subSql) {
        StringBuffer finalSb = new StringBuffer();
        finalSb.append("Select ");
        for (int i = 0, l = fieldList.size(); i < l; i++) {
            finalSb.append(fieldList.get(i));

            if (i + 1 < l) {
                finalSb.append(", ");
            }
        }
        finalSb.append(" from ( ").append(subSql).append(") ");
        return finalSb.toString();
    }

  /*  public String searchData(String conceptModeId, String fields, String condition, String order) {
        ConceptModel conceptModel = conceptModelService.findById(conceptModeId);
        if (conceptModel == null) {
            throw new AppException(ExceptionCode.Search.INVALID_SEARCH_PARAM, "conceptModeId invalid.");
        }
        return searchData(conceptModel.getProjectId(), conceptModel.getCode(), fields, condition, order);
    }*/


}
